package com.boarsoft.boar.gateway.web;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.proxy.AsyncProxyServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;

import com.boarsoft.common.Util;
import com.boarsoft.common.util.HttpClientUtil;
import com.boarsoft.flow.core.bean.ProcessHandler;
import com.boarsoft.soagov.svc.ServiceFinder;

/**
 * 
 * @author Mac_J
 *
 */
public class GatewayPHServlet extends AsyncProxyServlet {
	private static final long serialVersionUID = -4634847690862338373L;
	private static final Logger log = LoggerFactory.getLogger(GatewayPHServlet.class);

	@Autowired
	protected ApplicationContext applicationContext;
	/** */
	@Autowired
	protected ServiceFinder serviceFinder;
	@Autowired
	protected Environment enviroment;
	@Autowired
	@Qualifier("defaultPHMock")
	protected ProcessHandler defaultPHMock;

	@Value("${default.version}")
	protected String defaultVersion;
	@Value("${web.context}")
	protected String webContext;

	protected Map<String, ProcessHandler> proxyMap = new ConcurrentHashMap<String, ProcessHandler>();

	@Override
	protected void service(final HttpServletRequest req, final HttpServletResponse rsp) throws ServletException, IOException {
		String group = req.getHeader("group");
		String name = req.getHeader("name");// 暂不使用
		String version = req.getHeader("version");
		if (Util.strIsEmpty(version)) {
			version = defaultVersion;
		}
		String entry = req.getHeader("entry");
		String uri = req.getRequestURI();

		// 根据版本号全线回退：通过properties配置检查此版本号是否有配置回退版本
		// 如果有，通过修改版本号，回退此版本的所有服务到指定版本。
		version = enviroment.getProperty(version, version);

		// 回退（转发）到旧版：如果针对该URL指定了回退版本号，则仅针对这个URL回退到旧版，即直接转发此请求到后台服务。
		String flag = enviroment.getProperty(uri);
		if ((Util.strIsEmpty(group) && Util.strIsEmpty(flag))//
				|| "rewrite".equalsIgnoreCase(flag)) {
			String url = enviroment.getProperty(req.getServletPath())//
					.concat(req.getRequestURI());
			// log.info("Rewrite target url to {}", url);
			req.setAttribute("rewriteUrl", url);
			super.service(req, rsp);
			return;
		}
		if ("forward".equalsIgnoreCase(flag)) {
			uri = webContext.concat(uri);
			RequestDispatcher rd = req.getRequestDispatcher(uri);
			rd.forward(req, rsp);
			return;
		}

		// 回退到指定版本：如果按group/name/version指定了版本号
		// 则只针对这个服务，通过修改版本号，回退到指定的版本。
		String key = new StringBuilder().append(group).append("/").append(name)//
				.append("/").append(version).toString();
		version = enviroment.getProperty(key, version);

		// 获取对应的Dubbo代理，不存在则报错
		ProcessHandler ph = this.getProxy(group, name, version);
		if (ph == null) {
			rsp.sendError(HttpStatus.NOT_IMPLEMENTED_501);
			return;
		}
		String data = null;
		try {
			data = HttpClientUtil.readStr(req.getInputStream(), "UTF-8");
		} catch (Throwable e) {
			log.error("Error on service {}/{}/{}/{}", group, name, version, entry, e);
			rsp.sendError(HttpStatus.BAD_REQUEST_400);
			return;
		}

		// 向服务编排层发起调用
		String rs = null;
		try {
			// 服务编排层可能返回正常响应或错误码，要么抛出异常对象
			rs = (String) ph.process(entry, data, null);
			rs = (rs == null ? "" : rs);
			rsp.setContentType("application/json");
			rsp.setContentLength(rs.length());
			rsp.getWriter().write(rs);
			rsp.flushBuffer();
		} catch (Throwable e) {
			log.error("Error on service {}/{}/{}/{}", group, name, version, entry, e);
			rsp.sendError(HttpStatus.INTERNAL_SERVER_ERROR_500);
		}
		return;
	}

	@Override
	protected String rewriteTarget(HttpServletRequest req) {
		return (String) req.getAttribute("rewriteUrl");
	}

	public ProcessHandler getProxy(String group, String name, String version) {
		String key = new StringBuilder().append(group).append("/").append(name)//
				.append("/").append(ProcessHandler.class.getName())//
				.append("/").append(version).toString();
		if (proxyMap.containsKey(key)) {
			return proxyMap.get(key);
		}
		ProcessHandler ph = defaultPHMock;
		synchronized (proxyMap) {
			if (proxyMap.containsKey(key)) {
				return proxyMap.get(key);
			}
			if (applicationContext.containsBean(key)) {
				ph = applicationContext.getBean(ProcessHandler.class, key);
			} else {
				try {
					ph = serviceFinder.getInvokeService(//
							ProcessHandler.class, group, name, version);
				} catch (IllegalStateException e) {
					log.error("Error on get service handler {}", key, e);
				}
			}
			proxyMap.put(key, ph);
		}
		return ph;
	}

}
